﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;

namespace AugustVisual
{
    public class ScrollViewerBarBinding : ScrollViewer
    {
        static ScrollViewerBarBinding()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerBarBinding), new FrameworkPropertyMetadata(typeof(ScrollViewerBarBinding)));
        }
        AugustDataView augustDataView = null;
        ScrollBar scrollbar = null;
      public  AugustVerticleStackPanel stackPanel = null;
        ScrollContentPresenter scrollContentPresenter = null;
        ContentControl lostFocusContentControl = null;

        internal int scrollNum = 0;
        internal int shownum = 1;
        ColumnsCollection columns;
        ItemCollection itemCollection;
        public event ScrollEventHandler ScrollChangedEventHandler;
        public event OffsetChangedEventHandler OffsetChanged;


        int PreScrollValue = -1;



        private int offset = -1;
        public int Offset
        {
            get
            {
                return offset;
            }
            private set
            {
                if (value != offset)
                {
                    offset = value;
                    OnOffsetChanged(this, offset);
                }
                else
                {

                }
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            if (augustDataView.Items.Count == 0)
            {
                return;
            }
            if (e.Delta > 0)
            {
                scrollNum--;
                if (scrollNum < 0)
                {
                    scrollNum = 0;
                }
                //ScrollToTop();
            }
            else
            {
                scrollNum++;
                if (scrollNum > augustDataView.Items.Count - shownum)
                {
                    scrollNum = augustDataView.Items.Count - shownum;
                }
                if (scrollNum >= 0)
                {
                    //double smalloffset = shownum * ChildSize.Height - scrollContentPresenter.DesiredSize.Height;
                    //ScrollToVerticalOffset(smalloffset);
                }
                else
                {
                    scrollNum = 0;
                    return;
                }
            }
            SetOffset(scrollNum);
        }

        public double ScrollHeightZ
        {
            get { return (double)GetValue(ScrollHeightZProperty); }
            set { SetValue(ScrollHeightZProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ScrollHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScrollHeightZProperty =
            DependencyProperty.Register("ScrollHeightZ", typeof(double), typeof(ScrollViewerBarBinding), new PropertyMetadata(10.0d));


        public double VerticleOffsetZ
        {
            get { return (double)GetValue(VerticleOffsetZProperty); }
            set { SetValue(VerticleOffsetZProperty, value); }
        }

        // Using a DependencyProperty as the backing store for VerticleOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty VerticleOffsetZProperty =
            DependencyProperty.Register("VerticleOffsetZ", typeof(double), typeof(ScrollViewerBarBinding), new PropertyMetadata(0.0d));

        public Visibility ComputeVerticleBarIsShow
        {
            get { return (Visibility)GetValue(ComputeVerticleBarIsShowProperty); }
            set { SetValue(ComputeVerticleBarIsShowProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ComputeVerticleBarIsShow.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ComputeVerticleBarIsShowProperty =
            DependencyProperty.Register("ComputeVerticleBarIsShow", typeof(Visibility), typeof(ScrollViewerBarBinding), new PropertyMetadata(Visibility.Collapsed));

        public double ViewPortHeightZ
        {

            get { return (double)GetValue(ViewPortHeightZProperty); }
            set { SetValue(ViewPortHeightZProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ViewPortHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ViewPortHeightZProperty =
            DependencyProperty.Register("ViewPortHeightZ", typeof(double), typeof(ScrollViewerBarBinding), new PropertyMetadata(10.0d));


        public ScrollViewerBarBinding()
        {
            Loaded += ScrollViewerBarBinding_Loaded;
        }

        private void ScrollViewerBarBinding_Loaded(object sender, RoutedEventArgs e)
        {
            if (augustDataView == null)
            {
                augustDataView = FrameworkElementHelper.FindParent<AugustDataView>(this, 1, null);
                if (augustDataView != null)
                {
                    augustDataView.NotifyCollectionChanged += AugustDataView_NotifyCollectionChanged;
                    columns = augustDataView.Columns;
                    itemCollection = augustDataView.Items;
                }

            }
            if (scrollbar == null)
            {
                scrollbar = (ScrollBar)FrameworkElementHelper.Findchild(this, "PART_VerticalScrollBar2");
                if (scrollbar != null)
                {
                    scrollbar.Scroll += Scrollbar_Scroll;
                    scrollbar.SmallChange = 1;
                    scrollbar.LargeChange = 1;
                }
            }
            if (stackPanel == null)
            {
                stackPanel = FrameworkElementHelper.Findchild(this, "VisualizingStackPanel") as AugustVerticleStackPanel;
                if (stackPanel != null)
                {

                }
            }

            if (scrollContentPresenter == null)
            {
                scrollContentPresenter = FrameworkElementHelper.Findchild(this, "PART_ScrollContentPresenter") as ScrollContentPresenter;
                if (scrollContentPresenter != null)
                {
                    scrollContentPresenter.SizeChanged += ScrollViewBarbingding_SizeChanged;
                }
            }


            if (lostFocusContentControl == null)
            {
                lostFocusContentControl = FrameworkElementHelper.Findchild(this, "LostFocusContent") as ContentControl;
                if (lostFocusContentControl != null)
                {
                }
            }
        }

        public void SetOffset(int offset)
        {
            Offset = offset;
            UpDataBarVisible();
            if (offset > PreScrollValue && PreScrollValue != -1)
            {
                double smalloffset = shownum * ChildSize.Height - scrollContentPresenter.DesiredSize.Height;
                ScrollToVerticalOffset(smalloffset);
            }
            else if (offset < PreScrollValue)
            {
                ScrollToTop();
            }

            PreScrollValue = offset;
            if (ScrollHeightZ < offset)
            {
                offset = (int)ScrollHeightZ;
            }
            scrollbar.Value = offset;
            scrollNum = offset;
            lostFocusContentControl.Focus();
            for (int i = 0; i < shownum; i++)
            {
                if (offset + i < itemCollection.Count)
                {
                    ContactDataContext((ContentControl)(stackPanel.InternalChildren[i]), itemCollection[offset + i]);
                    if (augustDataView.FocusedObject == itemCollection[offset + i])
                    {
                        FrameworkElement[] rowCells = FrameworkElementHelper.Findchilds((FrameworkElement)stackPanel.InternalChildren[i], typeof(RowCell));
                        if (rowCells.Length > 0)
                        {
                            for (int j = 0; j < rowCells.Length; j++)
                            {
                                if (((RowCell)rowCells[j]).RelativeColum == augustDataView.FocusedColumn)
                                {
                                    rowCells[j].Focus();
                                }
                            }
                        }
                        else
                        {
                            //((ContentControl)stackPanel.InternalChildren[i]).Loaded -= ContentControl_Loaded;
                            ((ContentControl)stackPanel.InternalChildren[i]).Loaded += ContentControl_Loaded;
                        }
                    }
                }
            }
        }

        private void ContentControl_Loaded(object sender, RoutedEventArgs e)
        {
            FrameworkElement[] rowCells = FrameworkElementHelper.Findchilds((FrameworkElement)sender, typeof(RowCell));
            if (rowCells.Length == 0)
            {
            }
            for (int j = 0; j < rowCells.Length; j++)
            {
                if (((RowCell)rowCells[j]).RelativeColum == augustDataView.FocusedColumn)
                {
                    rowCells[j].Focus();

                }
            }
            ((ContentControl)sender).Loaded -= ContentControl_Loaded;
        }

        public new void ScrollToBottom()
        {
            SetOffset((int)ScrollHeightZ);
        }

        public virtual void OnOffsetChanged(object sender, int offset)
        {
            OffsetChanged?.Invoke(sender, offset);

        }

        public Size ChildSize
        {
            get
            {
                if (augustDataView.Items.Count == 0)
                {
                    return new Size();
                }
                if (stackPanel.InternalChildren.Count == 0)
                {
                    RealizeOneContainer();
                    ContactDataContext((FrameworkElement)stackPanel.InternalChildren[0], augustDataView.Items[0]);
                }
                stackPanel.InternalChildren[0].Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Size size = stackPanel.InternalChildren[0].DesiredSize;
                return size;
            }
        }

        private void ScrollViewBarbingding_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (itemCollection.Count > 0)
            {
                RealizeAllContainer();
                SetOffset(scrollNum);
            }
        }

        public void RealizeOneContainer()
        {
            AddChild();
        }

        int PreshowCount = 0;
        public void RealizeAllContainer()
        {
            //ScrollToVerticalOffset(0);
            int showcount = ComputeShowCount();
            int intrealized = stackPanel.InternalChildren.Count;
            int diff = showcount - PreshowCount;
            if (diff < 0)
            {
                for (int i = 0; i < PreshowCount - showcount; i++)
                {
                    stackPanel.InternalChildren[i + showcount].Visibility = Visibility.Hidden;
                }
            }
            else
            {
                for (int i = 0; i < showcount - intrealized; i++)
                {
                    AddChild();
                }
                for (int i = 0; i < showcount; i++)
                {
                    stackPanel.InternalChildren[i].Visibility = Visibility.Visible;
                }
            }
            PreshowCount = showcount;
        }

        public void ReUseAllContainer(UIElement[] uIElements)
        {
            int showcount = ComputeShowCount();
            int intrealized = stackPanel.InternalChildren.Count;
            int diff = showcount - PreshowCount;
            if (diff < 0)
            {
                for (int i = 0; i < PreshowCount - showcount; i++)
                {
                    stackPanel.InternalChildren[i + showcount].Visibility = Visibility.Hidden;
                }
            }
            else
            {
                for (int i = 0; i < showcount - intrealized; i++)
                {
                    stackPanel.AddInternalChild(uIElements[i]);
                }
                for (int i = 0; i < showcount; i++)
                {
                    stackPanel.InternalChildren[i].Visibility = Visibility.Visible;
                }
            }
            PreshowCount = showcount;

        }

        public void AddChild()
        {
            FrameworkElement contentControl = GenerateNewChild();
            stackPanel.AddInternalChild(contentControl);
        }

        List<FrameworkElement> frameworkElementsPrepare = new List<FrameworkElement>();
        Stopwatch stopwatch = new Stopwatch();
        public void PrepareChild()
        {
            stopwatch.Restart();
            for (int i = 0; i < 50; i++)
            {
                frameworkElementsPrepare.Add(GenerateNewChild());
                stackPanel.AddInternalChild(frameworkElementsPrepare[i]);
            }
            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);
        }

        private FrameworkElement GenerateNewChild()
        {
            ContentControl itemsControlchild = new ContentControl();
            ControlTemplate controlTemplate = new ControlTemplate();
            FrameworkElementFactory facStack = new FrameworkElementFactory(typeof(StackPanel));
            facStack.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
            FrameworkElementFactory facItempanel = new FrameworkElementFactory(typeof(RowLine));
            facItempanel.SetValue(NameProperty, "Row_Line");
            FrameworkElementFactory distinctTwoLine = new FrameworkElementFactory(typeof(Grid));
            distinctTwoLine.SetValue(NameProperty, "DistinctTwoLine");
            FrameworkElementFactory markRowLine = new FrameworkElementFactory(typeof(Grid));
            markRowLine.SetValue(NameProperty, "MarkRowLine");
            FrameworkElementFactory focusGrid = new FrameworkElementFactory(typeof(Grid));
            focusGrid.SetValue(NameProperty, "FocusRowLine");
            //focusGrid.SetValue(BackgroundProperty,Brushes.Orange);
            facItempanel.AppendChild(distinctTwoLine);
            facItempanel.AppendChild(markRowLine);
            //distinctTwoLine.SetValue(BackgroundProperty,Brushes.Orange);
            facItempanel.AppendChild(focusGrid);
            facItempanel.AppendChild(facStack);
          
            for (int j = 0; j < columns.Count; j++)
            {
                FrameworkElementFactory facTextBlock = new FrameworkElementFactory(typeof(RowCell));
                Binding binding = new Binding();
                binding.Path = new PropertyPath($"Columns[{j}].Width");
                binding.Mode = BindingMode.TwoWay;
                binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(AugustDataView), 1);
                facTextBlock.SetValue(RowCell.WidthProperty, binding);
                facTextBlock.SetValue(RowCell.ContentProperty, ((ColumBase)columns[j]).DisplayMemberBinding);
                facTextBlock.SetValue(RowCell.RelativeColumProperty, (ColumBase)columns[j]);

                //FrameworkElementFactory markBackGround = new FrameworkElementFactory(typeof(Grid));
                //facTextBlock.AppendChild(markBackGround);
                facStack.AppendChild(facTextBlock);
            }
            controlTemplate.VisualTree = facItempanel;
            itemsControlchild.Template = controlTemplate;
            return itemsControlchild;
        }


        private void ContactDataContext(FrameworkElement frameElement, object dataContext)
        {
            frameElement.DataContext = dataContext;
        }

        public int ComputeShowCount()
        {
            if (augustDataView.Items.Count > 0)
            {
                int shownum = (int)(scrollContentPresenter.ActualHeight / ChildSize.Height) + 1;
                if (itemCollection.Count < shownum)
                {
                    shownum = augustDataView.Items.Count;
                }
                this.shownum = shownum;
                return shownum;
            }
            else
            {
                return 0;
            }
        }
        public void UpDataBarVisible()
        {
            int shownum = ComputeShowCount();
            if (shownum < augustDataView.Items.Count || ChildSize.Height * shownum > scrollContentPresenter.ActualHeight)
            {
                ComputeVerticleBarIsShow = Visibility.Visible;
            }
            else
            {
                ComputeVerticleBarIsShow = Visibility.Collapsed;
            }
            ScrollHeightZ = itemCollection.Count - shownum;
            ViewPortHeightZ = ScrollHeightZ / 10;
        }

        private void Scrollbar_Scroll(object sender, ScrollEventArgs e)
        {
            ScrollChangedEventHandler?.Invoke(this, e);
            if (stackPanel != null)
            {
                SetOffset((int)e.NewValue);
            }
            //ScrollToTop();
        }

        private void AugustDataView_NotifyCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (((AugustDataView)sender).Items.Count > 0)
            {
                RealizeAllContainer();
                SetOffset(scrollNum);
            }
        }
    }
}